package com.zon.len.mp.extend.wrapper;

import static com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA;
import static java.util.stream.Collectors.joining;

import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.zon.len.mp.extend.matedata.TableMetadataHelper;
import com.zon.len.mp.extend.script.AggregationFunction;
import com.zon.len.mp.extend.sharding.AbstractTableShardingStrategy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import lombok.Getter;

//如下代码事例: 创建查询的wrapper
//   final LambdaExtendWrapper<MasterTable> wrapper = new LambdaExtendWrapper<>(MasterTable.class);
//   主表的查询条件
//   wrapper.eq(MasterTable::getId, 1)
//     进行 RIGHT JOIN 关联查询
//     .rightJoin(JoinTable.class,
//     JOIN ON的条件
//     jw ->jw.eq(JoinTable::getUserId, MasterTable::getId)
//       .or(sjw -> sjw.eq(JoinTable::getAmount, 100)
//       .eq(JoinTable::getUserId, MasterTable::getId))
//       将子表的查询字段放在查询列上(单表查询会自动过滤，不会查询)
//       .select(JoinTable::getAmount)
//       对子表进行自定义分库分表策略
//       .shardingStrategy(new JoinTableShardingStrategy()))
//     主表自定义分库分表策略
//     .shardingStrategy(new MasterTableShardingStrategy())
//     where条件进行子表条件(条件不会过滤，如果单表查询添加了则会直接报错)
//     .eq(JoinTable.class, JoinTable::getAmount, 100);

/**
 * @author ZonLen since on 2021/12/18 下午8:11
 * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
 */
public abstract class AbstractExtendWrapper<T, Children extends AbstractExtendWrapper<T, Children>>
    extends AbstractLambdaWrapper<T, Children> implements
    Query<Children, T, SFunction<T, ?>> {

  /**
   * 表名
   */
  protected String finalTableName;

  /**
   * join 表添加到where条件中
   */
  @Getter
  private final List<List<ISqlSegment>> joinTableWhereCondition = new ArrayList<>();

  /**
   * 自定义分表策略，优先级高于@TableSharding注解方式
   */
  public Children shardingStrategy(AbstractTableShardingStrategy<T> tableShardingStrategy) {
    if (tableShardingStrategy != null) {
      this.finalTableName = TableMetadataHelper
          .finalTableName(getEntityClass(), tableShardingStrategy);
    }
    return typedThis;
  }

  /**
   * 查询字段
   *
   * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
   */
  @SafeVarargs
  @Override
  public final Children select(SFunction<T, ?>... columns) {
    if (ArrayUtils.isNotEmpty(columns)) {
      final String selectColumns = Arrays
          .stream(columns).map(i -> columnToString(i, false)).collect(joining(COMMA));
      selectColumns(selectColumns);
    }
    return typedThis;
  }

  /**
   * 聚合函数的查询 COUNT(xxx) AS total; SUM(xxx) AS totalAmount 等等
   */
  public final Children select(SFunction<T, ?> column, String alias,
      AggregationFunction aggregationFunction) {
    String format = aggregationFunction.format(columnToString(column, false));
    if (StringUtils.isNotBlank(alias)) {
      format = format + Constants.AS + alias;
    }
    selectColumns(format);
    return typedThis;
  }

  /**
   * 查询字段
   *
   * @see com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
   */
  @Override
  public Children select(Class<T> tableClass, Predicate<TableFieldInfo> predicate) {
    selectColumns(
        TableMetadataHelper.tableMetadata(tableClass).getSelectColumnsForAlias(predicate));
    return typedThis;
  }

  private void selectColumns(String columns) {
    String selectColumns;
    final String sqlSelectColumns = sqlSelect.getStringValue();
    if (StringUtils.isNotBlank(sqlSelectColumns)) {
      selectColumns = sqlSelectColumns + COMMA;
    } else {
      selectColumns = columns;
    }
    if (this instanceof LambdaJoinWrapper) {
      ((LambdaJoinWrapper<?, T>) this).masterTableWrapper.addJoinSelect(selectColumns);
    } else {
      sqlSelect.setStringValue(selectColumns);
    }
  }

  /**
   * 获取表名
   */
  public String getFinalTableName() {
    return finalTableName;
  }

  protected AbstractExtendWrapper(Class<T> tableClass) {
    super.initNeed();
    this.finalTableName = TableMetadataHelper.globalFinalTableName(tableClass);
  }

  /**
   * 查询字段
   */
  protected SharedString sqlSelect = SharedString.emptyString();

  /**
   * 获取字段名
   */
  @Override
  protected String columnToString(SFunction<T, ?> column) {
    return columnToString(column, true);
  }

  /**
   * 获取字段名
   */
  @Override
  protected String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
    final String columnToString = super.columnToString(column, onlyColumn);
    if (StringUtils.isNotBlank(columnToString)) {
      return TableMetadataHelper.tableAlias(getEntityClass()) + StringPool.DOT + columnToString;
    }
    return columnToString;
  }

  @Override
  public void clear() {
    super.clear();
  }
}
